package com.example.obs.service.permission;

import com.example.obs.dao.DepartmentMapper;
import com.example.obs.dao.FileMapper;
import com.example.obs.dao.PermissionMapper;
import com.example.obs.model.domin.Department;
import com.example.obs.model.domin.ObsFile;
import com.example.obs.model.domin.Permission;
import com.example.obs.model.domin.User;
import com.example.obs.utils.UserUtils;
import com.example.obs.vo.PermissionSetInput;
import com.example.obs.vo.ResultVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;

/**
 * 资源库权限控制
 * @author Kevin
 * @date 2020-09-23 15:33
 */
@Slf4j
@Service
public class PermissionServiceImpl implements IPermissionService {

    /** 身份 */
    private static final Integer ROLE_ADMIN = 1;
    private static final Integer ROLE_SP_ADMIN = 2;
    /** 文件夹分隔符 */
    private static final String FILE_SPLIT = "/";
    /** 资源库前缀 */
    private static final String RESOURCE_BASE = "部门";
    private static final String RESOURCE_BASE_SPLIT = "部门/";
    /** 部门文件夹层数 */
    private static final Integer RESOURCE_COMMON_DIR_NUM = 2;
    /** 文件默认权限 */
    private static final Integer DEFAULT_FILE_PERMISSION = 2;

    private static final Integer FUNCTION_CHECK_ACCESS = 0;
    private static final Integer FUNCTION_CHECK_TRUE = 1;
    private static final Integer FUNCTION_CHECK_FALSE = 2;

    /** 上传、下载（查看）、删除、创建文件夹、分享、重命名 权限（储存位） */
    private static final int UPLOAD = 0;
    private static final int DOWNLOAD = 1;
    private static final int DELETE = 2;
    private static final int MKDIR = 3;
    private static final int SHARE = 4;
//    private static final int RENAME = 5;

    private final FileMapper fileMapper;
    private final PermissionMapper permissionMapper;
    private final DepartmentMapper departmentMapper;

    public PermissionServiceImpl(FileMapper fileMapper, PermissionMapper permissionMapper, DepartmentMapper departmentMapper) {
        this.fileMapper = fileMapper;
        this.permissionMapper = permissionMapper;
        this.departmentMapper = departmentMapper;
    }

    private boolean accessCheck(User currentUser, String path) {
        // 超管 直接放行
        if (ROLE_SP_ADMIN.equals(currentUser.getRole())) {
            return true;
        }
        // 管理员操作自己部门的文件 放行
        if (ROLE_ADMIN.equals(currentUser.getRole())) {
            Department department = departmentMapper.selectDepartmentById(currentUser.getDepartmentId());
            if (department != null && path.indexOf(RESOURCE_BASE_SPLIT + department.getDepartmentName()) == 0) {
                return true;
            }
        }
        // 非资源库文件不校验权限
        return !RESOURCE_BASE.equals(path.substring(0, RESOURCE_BASE.length()));
    }

    /**
     * 权限检查
     * @param path 文件/文件夹路径
     * @param direction 是否为文件夹
     * @param requirePermission 需要检查的权限
     * @param function 回调检查接口，0继续，1返回true，2返回false
     * @return 是否有权限
     **/
    private boolean commonCheck(String path, Boolean direction, int requirePermission, Function<String[], Integer> function) {
        User currentUser = UserUtils.getCurrentUser();
        // 放行检测
        if (accessCheck(currentUser, path)) {
            return true;
        }
        String[] dirNames = path.split("/");
        Integer check = function.apply(dirNames);
        if (FUNCTION_CHECK_TRUE.equals(check)) {
            return true;
        } else if (FUNCTION_CHECK_FALSE.equals(check)) {
            return false;
        }
        int permission = getPermission(currentUser, dirNames, direction);
        return ((permission >> requirePermission) & 1) == 1;
    }

    /**
     * 获取文件/文件夹权限
     * @param currentUser 当前登录用户
     * @param dirNames 文件/文件夹路径及其父级目录路径
     * @param direction 是否为文件夹
     * @return 返回权限
     **/
    private Integer getPermission(User currentUser, String[] dirNames, boolean direction) {
        List<String> dirPaths = new ArrayList<>(dirNames.length - 1);
        // 部门/XXX部门
        dirPaths.add(dirNames[0] + "/" + dirNames[1]);
        for (int i=2; i<dirNames.length; i++) {
            dirPaths.add(dirPaths.get(dirPaths.size() - 1) + "/" + dirNames[i]);
        }

        Integer permission = fileMapper.selectPermissionByPathAndDepartment(dirPaths, currentUser.getDepartmentId(), direction);
        if (permission == null) {
            log.error("【权限检查】找不到目标文件/文件夹权限，{}", (Object) dirNames);
        }
        return permission;
    }

    @Override
    public boolean upload(String path) {
        return commonCheck(path, true, UPLOAD, dirNames -> {
            // 公共目录无权限（部门/）
            if (dirNames.length < RESOURCE_COMMON_DIR_NUM) {
                return FUNCTION_CHECK_FALSE;
            }
            return FUNCTION_CHECK_ACCESS;
        });
    }

    @Override
    public boolean download(String path) {
        return commonCheck(path, false, DOWNLOAD, dirNames -> {
            // 资源库根目录及其部门根目录不校验
            if (dirNames.length <= RESOURCE_COMMON_DIR_NUM) {
                return FUNCTION_CHECK_TRUE;
            }
            return FUNCTION_CHECK_ACCESS;
        });
    }

    @Override
    public boolean delete(String path) {
        return commonCheck(path, false, DELETE, dirNames -> {
            // 资源库根目录及其部门根目录无权限
            if (dirNames.length <= RESOURCE_COMMON_DIR_NUM) {
                return FUNCTION_CHECK_FALSE;
            }
            return FUNCTION_CHECK_ACCESS;
        });
    }

    @Override
    public boolean mkdir(String path) {
        return commonCheck(path, true, MKDIR, dirNames -> {
            // 公共目录无权限（部门/）
            if (dirNames.length < RESOURCE_COMMON_DIR_NUM) {
                return FUNCTION_CHECK_FALSE;
            }
            return FUNCTION_CHECK_ACCESS;
        });
    }

    @Override
    public boolean share(String path) {
        return commonCheck(path, false, SHARE, dirNames -> {
            // 资源库根目录及其部门根目录无权限
            if (dirNames.length <= RESOURCE_COMMON_DIR_NUM) {
                return FUNCTION_CHECK_FALSE;
            }
            return FUNCTION_CHECK_ACCESS;
        });
    }

    @Override
    public Integer getPermission(Integer departmentId, Integer fileId) {
        return permissionMapper.selectPermissionByFileIdAndDepartmentId(fileId, departmentId);
    }

    @Override
    public ResultVO setPermission(PermissionSetInput input) {
        User currentUser = UserUtils.getCurrentUser();
        ObsFile obsFile = fileMapper.getFileByFileId(input.getFileId());
        if (!accessCheck(currentUser, obsFile.getPath())) {
            return ResultVO.error("您不能配置该文件权限");
        }
        // 部门id为空，设置文件/文件夹默认权限
        if (input.getDepartmentId() == null || input.getDepartmentId() == 0) {
            permissionMapper.updateFilePermissionById(input.getFileId(), input.getPermission());
            return ResultVO.success();
        }
        Permission permission = permissionMapper.selectRecordByFileIdAndDepartmentId(input.getFileId(), input.getDepartmentId());
        if (permission == null) {
            permission = new Permission(null, input.getFileId(), input.getDepartmentId(), input.getPermission());
            permissionMapper.insert(permission);
        } else {
            permissionMapper.updatePermissionByFileIdAndDepartmentId(input.getFileId(), input.getDepartmentId(), input.getPermission());
        }
        return ResultVO.success();
    }

    @Override
    public boolean setPermission(String path) {
        if (!RESOURCE_BASE.equals(path.substring(0, RESOURCE_BASE.length()))) {
            return false;
        }
        int index = path.indexOf(FILE_SPLIT, RESOURCE_BASE_SPLIT.length());
        if (index == -1) {
            return false;
        }
        String departmentName = path.substring(RESOURCE_BASE_SPLIT.length(), index);
        Department department = departmentMapper.selectDepartmentByName(departmentName);
        ObsFile obsFile = fileMapper.getFileByPath(0, path);
        if (department != null && obsFile != null) {
            Permission permission = new Permission(null, obsFile.getFileId(), department.getId(), DEFAULT_FILE_PERMISSION);
            return permissionMapper.insert(permission) > 0;
        }
        return false;
    }

}
